package hu.myai.util;

import hu.myai.calculators.Fibonacci;
import hu.myai.calculators.FibonacciFanMax;
import hu.myai.calculators.FibonacciFanMin;
import hu.myai.calculators.RateOfChange;
import hu.myai.calculators.WilliamsR;
import hu.myai.model.db.AxHistory;
import hu.myai.model.graph.DirectionNode;
import hu.myai.model.misc.KeyValue;
import hu.myai.model.sp.HighStockPivot;
import hu.myai.model.sp.LowStockPivot;
import hu.myai.model.sp.StockPivot;

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.joone.log.ILogger;
import org.joone.log.LoggerFactory;

public class Trend extends AbstractList<StockPivot> {

	private static final ILogger log = LoggerFactory.getLogger(Trend.class);

	private List<AxHistory> stockList = new ArrayList<AxHistory>();

	private List<StockPivot> stockPivotList = new ArrayList<StockPivot>();

	private StockPivot maxStock = null;

	private StockPivot minStock = null;

	// value of a main support
	private double mainSupportValue;

	// current stock symbol name
	private String symbol;

	private Fibonacci fibonacci;

	private FibonacciFanMax fibonacciFanMax;

	private FibonacciFanMin fibonacciFanMin;

	private RateOfChange rateOfChange;

	private WilliamsR williamsR;

	private DatabaseUtil database = new DatabaseUtil();

	public Trend(String symbol, List<AxHistory> stockList) {
		super();
		this.symbol = symbol;
		this.stockList = stockList;

	}

	@Override
	public StockPivot get(int index) {
		return this.stockPivotList.get(index);
	}

	@Override
	public boolean add(StockPivot stockPivot) {
		return this.stockPivotList.add(stockPivot);
	}

	@Override
	public int size() {
		return stockPivotList.size();
	}

	/**
	 * set the minSock (season low) and maxStock (season high)
	 */
	private void setMinMax() {

		DirectionNode dirNode_ll = (DirectionNode) Constans.bf_direction.getBean("direction_bottom");
		DirectionNode dirNode_lh = (DirectionNode) Constans.bf_direction.getBean("direction_top");
		DirectionNode dirNode_sl = (DirectionNode) Constans.bf_direction.getBean("direction_season_bottom");
		DirectionNode dirNode_sh = (DirectionNode) Constans.bf_direction.getBean("direction_season_top");

		// set minStock
		if (minStock == null) {
			minStock = new LowStockPivot(new Date(), Double.POSITIVE_INFINITY);
		} else {
			minStock.setDirectionNode(dirNode_ll);
		}

		// set maxStock
		if (maxStock == null) {
			maxStock = new HighStockPivot(new Date(), Double.NEGATIVE_INFINITY);
		} else {
			maxStock.setDirectionNode(dirNode_lh);
		}

		// for loop counter
		int i = 0;
		// size of the pivot list
		int j = stockPivotList.size();
		// for find the most effective support / resistance
		int maxAppearance = 0;

		for (StockPivot stockPivotItem : stockPivotList) {
			i++;

			// for support average calculation
			double sumDiff = 0.0;

			// get the min, earlier than a day
			if (stockPivotItem.getValue() < minStock.getValue()
					&& stockPivotItem.getLocation().getTime() < new Date().getTime() - 86400000) {
				minStock = stockPivotItem;
			}

			// get the max, earlier than a day
			if (stockPivotItem.getValue() > maxStock.getValue()
					&& stockPivotItem.getLocation().getTime() < new Date().getTime() - 86400000) {
				maxStock = stockPivotItem;
			}

			// for find the most effective support / resistance
			int counter = 0;
			List<StockPivot> sublist = stockPivotList.subList(i, j);
			// the distance from the resistance is supportTolerance
			for (StockPivot stockPivotItem2 : sublist) {

				double difference = stockPivotItem2.getValue() / stockPivotItem.getValue() - 1;
				if (Math.abs(difference) < Constans.supportTolerance) {
					sumDiff += difference;
					counter++;
				}
			}
			if (maxAppearance < counter) {
				maxAppearance = counter;
				mainSupportValue = stockPivotItem.getValue() * (1 + sumDiff / counter);
			}
		}

		// was it set in the for loop?
		if (minStock.getValue() != Double.POSITIVE_INFINITY) {
			minStock.setDirectionNode(dirNode_sl);
		} else {
			minStock = null;
		}

		// was it set in the for loop?
		if (maxStock.getValue() != Double.NEGATIVE_INFINITY) {
			maxStock.setDirectionNode(dirNode_sh);
		} else {
			maxStock = null;
		}
	}

	/**
	 * Do all calculation. Result is in the node attributes
	 */
	public void trendInit(int mode) {
		setMinMax();

		if (mode % 2 == 1) {
			fibonacci = new Fibonacci(symbol, maxStock, minStock, stockList, stockPivotList);
			fibonacci.calculator(0);
		}

		if ((mode >> 1) % 2 == 1) {
			fibonacciFanMax = new FibonacciFanMax(symbol, maxStock, minStock, stockList, stockPivotList);
			fibonacciFanMax.calculator(0);
		}

		if ((mode >> 2) % 2 == 1) {
			fibonacciFanMin = new FibonacciFanMin(symbol, maxStock, minStock, stockList, stockPivotList);
			fibonacciFanMin.calculator(0);
		}

		if ((mode >> 3) % 2 == 1) {
			rateOfChange = new RateOfChange(symbol, maxStock, minStock, stockList, stockPivotList);
			rateOfChange.calculator(10);
		}
		if ((mode >> 4) % 2 == 1) {
			williamsR = new WilliamsR(symbol, maxStock, minStock, stockList, stockPivotList);
			williamsR.calculator(10);
		}

	}

	public KeyValue[] getNext() {

		if (fibonacci == null || fibonacciFanMax == null || fibonacciFanMin == null) {
			log.error("Error, init the trend first");
			return null;
		}

		List<KeyValue[]> resultList = new ArrayList<KeyValue[]>();

		// levels next to last pivot
		resultList.add(fibonacci.getNexts());

		// levels next to last pivot
		resultList.add(fibonacciFanMax.getNexts());
		resultList.add(fibonacciFanMin.getNexts());

		Util util = new Util();
		return util.closestSupports(resultList);
	}

	@Override
	public String toString() {

		StringBuffer retValue = new StringBuffer();
		for (StockPivot stockPivot : stockPivotList) {
			retValue.append(stockPivot.toString()).append("\n");
		}
		return retValue.toString();
	}

	public List<AxHistory> getStockList() {
		return stockList;
	}

	public Fibonacci getFibonacci() {
		return fibonacci;
	}

	public FibonacciFanMax getFibonacciFanMax() {
		return fibonacciFanMax;
	}

	public FibonacciFanMin getFibonacciFanMin() {
		return fibonacciFanMin;
	}

	public RateOfChange getRateOfChange() {
		return rateOfChange;
	}

	public WilliamsR getWilliamsR() {
		return williamsR;
	}
}
